/*
 * Copyright (C) 2013 'Chaos'.Personal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.silly.adapter;

import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Pair;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.Checkable;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RatingBar;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.silly.adapter.entity.DataType;
import com.silly.adapter.loader.ViewLoader;
import com.silly.adapter.utils.RLog;

/**
 * Set object for View
 * 
 * @author Chaos
 * @date 2013-7-6
 */
public class SillyLoader {

	/** 单例构造 */
	private static SillyLoader instance = null;

	private SillyLoader(Context context) {
		this.mContext = context;
	}

	public static SillyLoader get(Context context) {
		if (instance == null) {
			instance = new SillyLoader(context);
		}
		return instance;
	}

	private Context mContext;

	// ------------------ Open Interface start ----------------
	/**
	 * 图片加载器
	 */
	private ImageLoader mImageLoader;

	public void setImageLoader(ImageLoader loader) {
		this.mImageLoader = loader;
	}

	private ViewBinder mBinder;

	public void setViewBinder(ViewBinder binder) {
		this.mBinder = binder;
	}

	// ------------------ Open Interface end ------------------
	public void setViewObjectList(View v, List<Object> dataList, int position) {
		if (dataList == null || dataList.size() == 0) {
			return;
		}

		for (Object obj : dataList) {
			setViewObject(v, obj, position);
		}
	}

	public void setViewObject(View v, Object data, int position) {

		if (v == null || data == null) {
			RLog.E.p("error...v or data is null at pos ->" + position);
			return;
		}

		// 拆分data，包含了Object-Type
		@SuppressWarnings("unchecked")
		Pair<Object, DataType> pair = (Pair<Object, DataType>) data;
		data = pair.first;
		DataType type = pair.second;

		boolean brk = setViewByType(v, data, type, position);
		if (brk) {
			return;
		}

		/**
		 * copy from {@link SimpleAdapter}
		 */
		boolean bound = false;
		if (mBinder != null) {
			bound = mBinder.setViewValue(position, v, data);
		}

		if (bound) {
			return;
		}

		bound = setupListener(v, data);

		if (bound) {
			return;
		}

		// keep the viewloader in the highest
		if (v instanceof ViewLoader) {

			ViewLoader loader = (ViewLoader) v;
			loader.setViewData(v, data, position);

		} else if (v instanceof Checkable) {

			setViewCheckable((Checkable) v, data);

		} else if (v instanceof Button) {

			setViewButton((Button) v, data);

		} else if (v instanceof ImageButton) {

			// Note: keep the instanceof ImageButton be Top of ImageView
			// since ImageButton is ImageView
			// TODO setButtonImage((ImageButton) v, text);
		} else if (v instanceof ImageView) {

			setViewImage(position, (ImageView) v, data);

		} else if (v instanceof RatingBar) {

			setViewRating((RatingBar) v, data);

		} else if (v instanceof TextView) {

			// Note: keep the instanceof TextView check at the bottom of
			// these if since a lot of views are TextViews (e.g.
			// CheckBoxes).
			setViewText(position, (TextView) v, data);

		} else if (v instanceof ProgressBar) {

			// Note: keep the instanceof ProgressBar check at the bottom of
			// these if since a lot of views are ProgressBar (e.g.
			// RatingBar).
			setViewProgressBar((ProgressBar) v, data, position);

		} else {
			throw new IllegalStateException(v.getClass().getName() + " is not a "
					+ " view that can be bounds by this CoreLoader");
		}
	}

	/**
	 * @param v
	 * @param data
	 * @param type
	 * @param position
	 * @return 返回true，表示不再往下进行。
	 */
	private boolean setViewByType(View v, Object data, DataType type, int position) {
		if (type == DataType.Auto) {
			// 自动进行 自动匹配模式
			return false;
		} else if (type == DataType.Tag) {
			v.setTag(data);
			return true;
		} else if (type == DataType.Background) {
            setBackground(v, data);
            return true;
		}
		return false;
	}

	private void setBackground(View v, Object data) {
        if(data instanceof Integer){
        	int resId = (Integer) data;
        	v.setBackgroundResource(resId);
        }else if(data instanceof Drawable){
        	v.setBackground((Drawable) data);
        }else if(data instanceof Bitmap){
        	Drawable background = new BitmapDrawable(mContext.getResources(), (Bitmap) data);
        	v.setBackground(background);
        }else if(data instanceof String){
        	checkNullThenThrow(mImageLoader, "if you will download image , make sure the ImageLoader is not null");
			Bitmap res = mImageLoader.loadImage(null, data.toString());
			Drawable background = new BitmapDrawable(mContext.getResources(), res);
        	v.setBackground(background);
        }
	}

	private void setViewProgressBar(ProgressBar bar, Object data, int position) {

		if (data instanceof Integer) {
			int progress = (Integer) data;
			bar.setProgress(progress);
		} else if (data instanceof Drawable) {
			Drawable drawable = (Drawable) data;
			bar.setProgressDrawable(drawable);
		}
	}

	/*
	 * Set the value for RatingBar
	 */
	private void setViewRating(RatingBar v, Object rating) {
		if (rating instanceof Float) {
			v.setRating((Float) rating);
		}
	}

	/*
	 * Set text value for TextView
	 */
	private void setViewText(int position, TextView v, Object text) {

		if (text instanceof byte[]) {

			v.setText(new String((byte[]) text));

		} else if (text instanceof CharSequence) {

			v.setText((CharSequence) text);

		} else if (text instanceof Integer) {

			int resId = (Integer) text;
			CharSequence content = mContext.getResources().getText(resId);
			v.setText(content);
		}
	}

	/*
	 * Set drawable value for ImageView
	 */
	private void setViewImage(int position, ImageView v, Object obj) {
		Drawable oldDrawable = v.getDrawable();
		if (oldDrawable != null) {
			// clear the CALLBACK reference to prevent of OOM error
			oldDrawable.setCallback(null);
		}

		if (obj instanceof Drawable) {
			// here is one drawable object
			v.setImageDrawable((Drawable) obj);

		} else if (obj instanceof Bitmap) {
			// here is one bitmap object
			v.setImageBitmap((Bitmap) obj);

		} else if (obj instanceof String) {
			// here is one remote object (URL)
			checkNullThenThrow(mImageLoader, "if you will download image , make sure the ImageLoader is not null");
			Bitmap res = mImageLoader.loadImage(v, obj.toString());
			if (res != null) {
				v.setImageBitmap(res);
			}

		} else if (obj instanceof Boolean) {

			if ((Boolean) obj) {
				v.setVisibility(View.VISIBLE);
			} else {
				v.setVisibility(View.INVISIBLE);
			}
		} else if (obj instanceof Integer) {
			int resId = (Integer) obj;
			v.setImageResource(resId);
		}
	}

	/**
	 * Detecting whether an element that not allow null but is null...
	 * 
	 * @param obj
	 * @param cause
	 */
	private void checkNullThenThrow(Object obj, String cause) {
		if (obj == null) {
			throw new RuntimeException(cause);
		}
	}

	private void setViewCheckable(Checkable v, Object data) {
		if (data instanceof Boolean) {
			((Checkable) v).setChecked((Boolean) data);
		} else {
			throw new IllegalStateException(v.getClass().getName() + " should be bound to a Boolean, not a "
					+ data.getClass());
		}
	}

	private void setViewButton(Button v, Object data) {

		if (data instanceof Integer) {
			int resId = (Integer) data;
			CharSequence content = mContext.getResources().getText(resId);
			v.setText(content);
		} else if (data instanceof CharSequence) {
			v.setText((CharSequence) data);
		}
	}

	private boolean setupListener(View v, Object data) {
		if (data instanceof OnClickListener) {
			v.setOnClickListener((OnClickListener) data);
			return true;
		} else if (data instanceof OnLongClickListener) {
			v.setOnLongClickListener((OnLongClickListener) data);
			return true;
		} else if (data instanceof OnTouchListener) {
			v.setOnTouchListener((OnTouchListener) data);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * if you will download image for some view,you must {@link #setImageLoader}
	 * for {@link SillyAdapter}
	 * 
	 * @author Chaos
	 * @date 2013-7-3
	 * 
	 */
	public static interface ImageLoader {
		/**
		 * This method will run in the main thread, make sure to not waste time
		 * in this thread is too long... you can use {@link Thread} or
		 * {@link AsyncTask} to avoid this problem.
		 * 
		 * @param iv (Maybe null,if {@link DataType#Background} Mode...})
		 * @param url
		 * @return if is not null,the {@link SillyAdapter} will be execute
		 *         {@link ImageView#setImageBitmap(Bitmap)},if you execute
		 *         {@link ImageView#setImageBitmap(Bitmap)} please return null;
		 */
		public Bitmap loadImage(ImageView iv, String url);
	}

	/**
	 * This class can be used by external clients of SimpleAdapter to bind
	 * values to views.
	 * 
	 * You should use this class to bind values to views that are not directly
	 * supported by SimpleAdapter or to change the way binding occurs for views
	 * supported by SimpleAdapter.
	 * 
	 * @see SimpleAdapter#setViewImage(ImageView, int)
	 * @see SimpleAdapter#setViewImage(ImageView, String)
	 * @see SimpleAdapter#setViewText(TextView, String)
	 */
	public static interface ViewBinder {
		/**
		 * Binds the specified data to the specified view.
		 * 
		 * When binding is handled by this ViewBinder, this method must return
		 * true. If this method returns false, SimpleAdapter will attempts to
		 * handle the binding on its own.
		 * 
		 * @param view
		 *            the view to bind the data to
		 * @param data
		 *            the data to bind to the view
		 * @param textRepresentation
		 *            a safe String representation of the supplied data: it is
		 *            either the result of data.toString() or an empty String
		 *            but it is never null
		 * 
		 * @return true if the data was bound to the view, false otherwise
		 */
		boolean setViewValue(int position, View view, Object data);
	}
}
